package tlog

import (
	"context"
	"fmt"
	"path/filepath"
	"runtime"
	"strings"
)

// should be assigned when service init
var Handler *CountLogger

type ICounter interface{
	Count(metricName string)
	CounterByTags(metricName string, tags map[string]string)
}

type ILogger interface {
	/*Trace(tag string, args ...interface{})
	Tracef(ctx context.Context, tag string, format string, args ...interface{})
	Warn(tag string, args ...interface{})
	Warnf(ctx context.Context, tag string, format string, args ...interface{})
	Fatal(tag string, args ...interface{})
	Fatalf(ctx context.Context, tag string, format string, args ...interface{})*/

	Debug(tag string, args ...interface{})
	Debugf(ctx context.Context, tag string, format string, args ...interface{})

	Info(tag string, args ...interface{})
	Infof(ctx context.Context, tag string, format string, args ...interface{})

	Error(tag string, args ...interface{})
	Errorf(ctx context.Context, tag string, format string, args ...interface{})
	//Public(ctx context.Context, key string, pairs map[string]interface{}, isPLid bool)

	// ErrorCount ：print error log and send count to monitor server if exist
	// ctx : context.Context
	// tag: key which to be counted by
	//ErrorCount(ctx context.Context, metricName string, content string)

	// ErrorCountWithTags ：print error log and send count to monitor server if exist
	// ctx : context.Context
	// tag: keys which to be counted by
	//ErrorCountWithTags(ctx context.Context, metricName string, tags map[string]string, content string)
}

type CountLogger struct {
	log ILogger
	count ICounter
}

func SetCountLogger(logger ILogger, counter ICounter){
	Handler =  NewCountLogger(logger, counter)
}

func NewCountLogger(logger ILogger, counter ICounter) *CountLogger {
	return &CountLogger{
		log: logger,
		count: counter,
	}
}

func (c *CountLogger) Debug(tag string, args ...interface{}) {
	c.log.Debug(tag, args)
}

func (c *CountLogger) Debugf(ctx context.Context, tag string, format string, args ...interface{}) {
	c.log.Debugf(ctx, tag, format, args)
}

func (c *CountLogger) Info(tag string, args ...interface{}) {
	c.log.Info(tag, args)
}

func (c *CountLogger) Infof(ctx context.Context, tag string, format string, args ...interface{}) {
	c.log.Infof(ctx, tag, format, args)
}

func (c *CountLogger) Error(tag string, args ...interface{}) {
	c.log.Error(tag, args)
}

func (c *CountLogger) Errorf(ctx context.Context, tag string, format string, args ...interface{}) {
	c.log.Errorf(ctx, tag, format, args)
}

func (c *CountLogger) Public(ctx context.Context, key string, pairs map[string]interface{}, isPLid bool) {
	//公司内部业务日志打印专用，可根据业务需要在继承struct中自行实现，也可忽略
}

// ErrorCount ：print error log and send count to monitor server if exist
// ctx : context.Context
// tag: key which to be counted by
func (c *CountLogger) ErrorCount(ctx context.Context, metricName string, content string) {
	path := c.FormatLogPath()
	c.Errorf(ctx, metricName, "etype=%v||log_path=%v||error=%v", metricName, path, strings.Replace(content, "\n", "", -1))
	//按metricName上报错误统计，如果有就加，没有就省略吧。
	if c.count != nil {
		c.count.Count(metricName)
	}
}

// ErrorCountWithTags ：print error log and send count to monitor server if exist
// ctx : context.Context
// tag: keys which to be counted by
func (c *CountLogger) ErrorCountWithTags(ctx context.Context, metricName string, tags map[string]string, content string) {
	path := c.FormatLogPath()
	c.log.Errorf(ctx, metricName, "etype=%v||log_path=%v||error=%v", metricName, path, strings.Replace(content, "\n", "", -1))
	//按metricName上报错误统计，如果有就加，没有就省略吧。
	if c.count != nil {
		c.count.CounterByTags(metricName, tags)
	}
}

func (c *CountLogger) FormatLogPath() string {
	filename, line, path, ok, index := "", 0, "", false, 1
	for {
		if index >= 10 {
			break
		}
		_, filename, line, ok = runtime.Caller(index)
		if ok {
			filename = filepath.Base(filename)
		}
		if filename == "" {
			break
		}
		index++
		if strings.Contains(filename, "autogenerated") || strings.Contains(filename, "asm_amd64") {
			continue
		}
		if len(path) > 0 {
			path = filename + ":" + fmt.Sprintf("%v", line) + "/" + path
		} else {
			path = filename + ":" + fmt.Sprintf("%v", line)
		}
	}
	return path
}
